#!/usr/bin/env python3
import re
from pygls.server import LanguageServer
from typing import List, Optional, Union, Tuple
from lsprotocol.types import (
    TEXT_DOCUMENT_COMPLETION,
    TEXT_DOCUMENT_DEFINITION,
    TEXT_DOCUMENT_DID_OPEN,
    TEXT_DOCUMENT_HOVER,
    DidOpenTextDocumentParams,
    DefinitionOptions,
    DefinitionParams,
    Location,
    LocationLink,
    Position,
    Range,
    Hover,
    TextDocumentIdentifier,
    TextDocumentPositionParams,
)
from asm_parser import parse_workspace, search_in_workspace, Variable


class TestLspServer(LanguageServer):
    def __init__(self, *args) -> None:
        super().__init__(*args)
        #  self.logfile = open("/Users/jackzhang/workspace/verilog_lsp/log", "a+")
        self.file_workspace = {}

        @self.feature(TEXT_DOCUMENT_DID_OPEN)
        def did_open(params: DidOpenTextDocumentParams):
            root_path = self.workspace.root_path
            if root_path:
                self.file_workspace = parse_workspace(root_path)

        @self.feature(
            TEXT_DOCUMENT_DEFINITION,
            DefinitionOptions(),
        )
        def gotoDefinition(
            params: DefinitionParams,
        ):
            filename = params.text_document.uri[7:]
            lineno = params.position.line + 1
            name = self._cursor_word(
                params.text_document.uri, params.position)
            self._log("filename", filename)
            self._log("lineno", lineno)
            self._log("name", name)
            if name:
                name = name[0]
            else:
                return None
            variable : Variable = search_in_workspace(
                self.file_workspace, filename, lineno, name)
            if not variable:
                return None
            line = variable.line - 1
            pos = variable.pos - 1
            endpos = pos + len(name)
            return Location(
                uri=params.text_document.uri,
                range=Range(
                    start=Position(line=line, character=pos),
                    end=Position(line=line, character=endpos),
                ),
            )

        @self.feature(TEXT_DOCUMENT_HOVER)
        def hover(params: TextDocumentPositionParams) -> Optional[Hover]:
            filename = params.text_document.uri[7:]
            lineno = params.position.line + 1
            name = self._cursor_word(
                params.text_document.uri, params.position)
            if name:
                name = name[0]
            else:
                return None
            variable : Variable = search_in_workspace(
                self.file_workspace, filename, lineno, name)
            if not variable:
                return None
            doc = self.workspace.get_text_document(params.text_document.uri)
            content = doc.source
            line = content.split("\n")[variable.line - 1]
            return Hover(contents=line)

    def _cursor_line(self, uri: str, position: Position) -> str:
        doc = self.workspace.get_text_document(uri)
        content = doc.source
        line = content.split("\n")[position.line]
        return str(line)

    def _cursor_word(
        self, uri: str, position: Position
    ) -> Optional[Tuple[str, Range]]:
        line = self._cursor_line(uri, position)
        cursor = position.character
        for m in re.finditer(r"\w+", line):
            start = m.start()
            if m.start() <= cursor <= m.end():
                word = (
                    line[start: m.end()],
                    Range(
                        start=Position(line=position.line,
                                       character=start),
                        end=Position(line=position.line, character=m.end()),
                    ),
                )
                return word
        return None

    def _log(self, *args):
        log = "".join(map(str, args))
        with open("/Users/jackzhang/workspace/verilog_lsp/log", "a+") as file:
            file.write(log + "\n")

    def __exit__(self, a0, a1, a2):
        pass
        #  self.logfile.close()

TestLspServer("test-language-server", "0.1").start_io()
